#ifndef __GLF_CORE_ATOMIC_H_INCLUDED__
#define __GLF_CORE_ATOMIC_H_INCLUDED__

#include <glf/config.h>
#include <glf/core/mutex.h>
#include <glf/config/platform.h>

namespace glf
{


template<typename T>
class Atomic {
public:
	inline Atomic();
	inline Atomic(const T& value);
	inline ~Atomic();

	inline T fetchAndAdd(const T& value);
	inline T fetchAndSub(const T& value);
	inline T fetchAndOr(const T& value);
	inline T fetchAndAnd(const T& value);
	inline T fetchAndXor(const T& value);
	inline T fetchAndNand(const T& value);

	inline T addAndFetch(const T& value);
	inline T subAndFetch(const T& value);
	inline T orAndFetch(const T& value);
	inline T andAndFetch(const T& value);
	inline T xorAndFetch(const T& value);
	inline T nandAndFetch(const T& value);

	inline T operator++();
	inline T operator--();
	inline T operator++(int);
	inline T operator--(int);
	inline T operator=(const T&);
	inline operator T() const;

protected:
	T mValue;
};

#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
extern Mutex AtomicLock;
#endif

template<typename T>
Atomic<T>::Atomic() {
}

template<typename T>
Atomic<T>::Atomic(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	// NOTE: some compiler will trigger this assert even if the template is not used, so to
	//		avoid breaking compilation on those (LLVM for instance ...) we use the following trick.
	//		See the boost staticassert library documentation.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue = value;
}

template<typename T>
Atomic<T>::~Atomic() {
}

template<typename T>
T Atomic<T>::fetchAndAdd(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue += value;
	return oldValue;
}

template<typename T>
T Atomic<T>::fetchAndSub(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue -= value;
	return oldValue;
}

template<typename T>
T Atomic<T>::fetchAndOr(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue |= value;
	return oldValue;
}

template<typename T>
T Atomic<T>::fetchAndAnd(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue &= value;
	return oldValue;
}

template<typename T>
T Atomic<T>::fetchAndXor(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue ^= value;
	return oldValue;
}

template<typename T>
T Atomic<T>::fetchAndNand(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	T oldValue = mValue;
	mValue &= ~value;
	return oldValue;
}

template<typename T>
T Atomic<T>::addAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue += value;
	return mValue;
}

template<typename T>
T Atomic<T>::subAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue -= value;
	return mValue;
}

template<typename T>
T Atomic<T>::orAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue |= value;
	return mValue;
}

template<typename T>
T Atomic<T>::andAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue &= value;
	return mValue;
}

template<typename T>
T Atomic<T>::xorAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue ^= value;
	return mValue;
}

template<typename T>
T Atomic<T>::nandAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue &= ~value;
	return mValue;
}

template<typename T>
T Atomic<T>::operator=(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	mValue = value;
	return mValue;
}

template<typename T>
T Atomic<T>::operator ++() {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	return ++mValue;
}

template<typename T>
T Atomic<T>::operator --() {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	return --mValue;
}

template<typename T>
T Atomic<T>::operator ++(int) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	return mValue++;
}

template<typename T>
T Atomic<T>::operator --(int) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	return mValue--;
}

template<typename T>
Atomic<T>::operator T() const {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
#	if !defined(GLF_KEEP_DEPRECATED_ATOMIC)
	// No support for atomic; atomic behavior is done via Mutex which WE DO NOT RECOMMEND.
	// If you do not use multithreading, please define GLF_DISABLE_DEFAULT_ATOMIC_MUTEX.
	// If you do use multithreading, please report this error to glf-world.
	// You can define GLF_KEEP_DEPRECATED_ATOMIC to be able to compile while we fix you problem.
	GLF_STATIC_ASSERT(sizeof(T) == 0);
#	endif

	LockScope lock(AtomicLock);
#endif
	return mValue;
}

void Membar();

template<typename T>
inline T LoadWithAcquire(const T& ref)
{
	const T value = ref;
	glf::Membar();
	return value;
}

template<typename T, typename U>
inline void StoreWithRelease(T& ref, const U& value)
{
	glf::Membar();
	ref = value;
}

} // namespace glf


#if defined(SN_TARGET_PSP2)
#	include <glf/core/atomic_psp2_.h>
#else
#	define GLFi_ATOMIC_HEADER(path, api)	GLF_TOSTRING(GLFi_ATOMIC_HEADER2(path,api))
#	define GLFi_ATOMIC_HEADER2(path, api)	GLFi_ATOMIC_HEADER3(path,api)
#	define GLFi_ATOMIC_HEADER3(path, api)	path##_##api##_.h
#	if defined(GLF_PLATFORM_PLAYBOOK)
// 		Playbook libs does not define posix atomics...
// 		So if we use them we'll have undefined references, we'll use atomic from QNX lib
#		include GLFi_ATOMIC_HEADER(atomic, qnx)
#	else
#		include GLFi_ATOMIC_HEADER(atomic, GLF_API_THREAD)
#	endif
#endif

#endif //__GLF_CORE_ATOMIC_H_INCLUDED__
